對應 30天挑戰精通 PowerShell 該書第 17 章「輸出與輸入」。
本章的內容僅適用於與人類的視覺和觸覺進行互動的指令碼。對於自動執行且無需人工干預( unattended )的指令碼而言,這些技巧並不適合,因為不會有人在場與之互動。
Reference - P.275
雖說上述的解釋,似乎跟自動化扯不上邊,但是當我看到章節標題裡的 Write-Host 及 Write-Output 時,我心中還是跳出了疑惑,對耶,這兩個差在哪?讓我繼續看下去。
書中逐步解釋了 PowerShell 中的顯示和提示機制,並針對不同的環境提供具體說明,首先提到了 PowerShell 引擎以及主機應用程式這兩個名詞:
是實際執行 PowerShell 語法並生成結果的核心部分,你可以想像他就是一個強大的機器,它能夠執行各種命令並生成結果。
是你與 PowerShell 引擎進行互動的工具,它負責接收輸入(你的命令)以及顯示輸出(命令執行後的結果),把它想像成是控制這台機器的面板,你在面板上按的每一個按鈕(輸入命令),面板會展示執行後的結果(顯示輸出)。
而書中提出了三個不同類型的主機應用程式並解釋到:
而了解不同主機應用程式如何處理 PowerShell 的輸入和輸出,能夠幫助我們在不同的環境中正確預期命令的行為,避免混淆。例如:當你發現同樣的命令在本地執行和在 Azure 中的行為不同時,你會知道這是因為兩個不同的主機應用程式在工作,而不是命令本身有問題。
Read-Host cmdlet 用於從用戶那裡獲取輸入。它會在控制台上顯示一個提示( text prompt ),然後等待用戶輸入,並將輸入作為字符串返回,而下面的範例都把「獲得的輸入」存到一個變數中:
# --------- Example 1: Save console input to a variable ---------
$Age = Read-Host "Please enter your age"
# ------- Example 2: Save console input as a secure string -------
$pwd_secure_string = Read-Host "Enter a Password" -AsSecureString
# ------- Example 3: Mask input and as a plaintext string -------
$pwd_string = Read-Host "Enter a Password" -MaskInput
僅有 Windows 能夠執行該動作,像我用 macOS 嘗試還原它會因為SecureString
的功能在 macOS 上受限,無法像在 Windows 上那樣運作。
$pwd_secure_string = Read-Host "Enter a Password" -AsSecureString
$plain_password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto(
[System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($pwd_secure_string)
)
# Print the plain text password to confirm the result
Write-Output $plain_password
先來看看書中的解釋:
Write-Host 就像其他的 cmdlet 一樣在管線中執行,但它不會將任何內容放入管線內。它實際上做了兩件事情:寫入一筆紀錄到「資料流」( Information stream )中,並且直接寫入到主機應用程式的畫面上。
Reference - P.277 ~ P.278
而對我來說,Write-Host 用於將輸出直接寫到控制台(主機應用程式),它可以設置文本的顏色和背景色,非常適合用於顯示需要強調的信息,而書中也特別強調,Write-Host
並不是產生錯誤訊息、告警、除錯訊息等等的最佳方式,而建議我們可以改用 Write-Verbose
來處理,其原因是輸出結果會改傳到「詳細資訊流」( verbose stream )。
直到這,已經有兩個關鍵術語是我聽不懂的,資訊流( Information stream )及詳細資訊流( verbose stream ),不過沒關係,我先依循書中的章節規劃,如果看完這章節都沒有解釋,讓我來整理並彙整其解釋。
先讓我介紹一下使用 Write-Host
時,三個好用的參數:
BackgroundColor
指定其背景顏色
PS /Users/kanglin> get-help write-host -Parameter BackgroundColor
-BackgroundColor <System.ConsoleColor>
Specifies the background color. There is no default. The acceptable values for this parameter are:
- `Black`
- `DarkBlue`
- `DarkGreen`
- `DarkCyan`
- `DarkRed`
- `DarkMagenta`
- `DarkYellow`
- `Gray`
- `DarkGray`
- `Blue`
- `Green`
- `Cyan`
- `Red`
- `Magenta`
- `Yellow`
- `White`
Required? false
Position? named
Default value None
Accept pipeline input? False
Accept wildcard characters? false
ForegroundColor
指定文字顏色
PS /Users/kanglin> get-help write-host -Parameter ForegroundColor
-ForegroundColor <System.ConsoleColor>
Specifies the text color. There is no default. The acceptable values for this parameter are:
- `Black`
- `DarkBlue`
- `DarkGreen`
- `DarkCyan`
- `DarkRed`
- `DarkMagenta`
- `DarkYellow`
- `Gray`
- `DarkGray`
- `Blue`
- `Green`
- `Cyan`
- `Red`
- `Magenta`
- `Yellow`
- `White`
Required? false
Position? named
Default value None
Accept pipeline input? False
Accept wildcard characters? false
Separator
指定要在主機顯示的物件之間插入的分隔符號字串
PS /Users/kanglin> get-help write-host -Parameter Separator
-Separator <System.Object>
Specifies a separator string to insert between objects displayed by the host.
Required? false
Position? named
Default value None
Accept pipeline input? False
Accept wildcard characters? false
Write-Host (2,4,6,8,10,12) -Separator ", -> " -ForegroundColor DarkGreen -BackgroundColor White
先來看看書中的解釋:
不同於 Write-Host,Write-Output 可以將物件傳送進管線。因為他不是直接寫入到顯示器上,因此它無法讓你指定不同的顏色或其他任何設定。事實上,Write-Output(或是它的別名 Write )本質上不是用來顯示輸出結果的,正如我們所說,它將物件傳送進管線 — 最終是管線自己要負責顯示這些物件。下圖展示了運作的流程:
*Reference - P.280 *
看到這裡,我自己理解後的解釋是:Write-Output 將對象輸出到管道。而我們可以用另外的命令(例如 Where-Object
)將其重新 Filter 以影響最終呈現在終端機(主機應用程式)上的結果或將其賦值給變數(雖然我感受不到在什麼情況下會需要透過 Write-Output
去賦值)。
Write-Host
直接將輸出寫到終端機(主機應用程式),不參與管線,也不能被重定向或捕獲。
Write-Output
將輸出發送到管線,可以被下一個命令接收,或者被重定向或捕獲。
# 使用 Write-Host
$result = Write-Host "Hello, World!"
Write-Host $result # $result 將為 $null,因為輸出不能被捕獲
# 使用 Write-Output
$result = Write-Output "Hello, World!"
Write-Host $result # $result 將包含字符串 "Hello, World!"
書中提供了 Write
字眼的表格,我這邊透過 ChatGPT 整理了一份我認為更清楚的表格作為今天這章節的結尾:
Cmdlet 名稱 | 說明 | 相關變數設定 | 預設值 | 可能的選項 |
---|---|---|---|---|
Write-Host | 在控制台上顯示訊息,不傳遞到管道 | 無 | 無 | 無 |
Write-Output | 將物件寫入輸出管道,可被後續的管道命令接收 | 無 | 無 | 無 |
Write-Error | 將錯誤訊息寫入錯誤管道,會引發錯誤事件 | $ErrorActionPreference |
Continue |
Continue 、Stop 、SilentlyContinue 、Inquire |
Write-Warning | 將警告訊息寫入警告管道 | $WarningPreference |
Continue |
Continue 、SilentlyContinue 、Stop 、Inquire |
Write-Verbose | 輸出詳細訊息,通常用於提供腳本運行的詳細資訊 | $VerbosePreference |
SilentlyContinue |
SilentlyContinue 、Continue 、Stop 、Inquire |
Write-Debug | 輸出除錯訊息,用於腳本除錯階段 | $DebugPreference |
SilentlyContinue |
SilentlyContinue 、Continue 、Stop 、Inquire |
Write-Information | 將資訊訊息寫入資訊管道,可被特定偏好設定控制 | $InformationPreference |
SilentlyContinue |
SilentlyContinue 、Continue 、Stop 、Inquire |
Write-Progress | 顯示進度條,提供操作進度的視覺化反饋 | 無 | 無 | 無 |
$WarningPreference = "Continue"
Write-Warning "這是一條警告訊息。"
$VerbosePreference = "Continue"
Write-Verbose "這是一條詳細訊息。"
$DebugPreference = "Continue"
Write-Debug "這是一條除錯訊息。"
$InformationPreference = "Continue"
Write-Information "這是一條資訊訊息。"
for ($i = 0; $i -le 100; $i += 10) {
Write-Progress -Activity "正在處理" -Status "進度:$i%" -PercentComplete $i
Start-Sleep -Milliseconds 500
}
Day 21 - 工作階段:更輕鬆的遠端控制